﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WASapTKB.Domain
{
    public class InputData
    {
        /*
       ********* Giới hạn đầu vào *********
       ************************************
       * 10 nhóm lớp. Trong đó, 8 lớp có sỉ số lá 50 và 2 lớp có chỉ số là 100.
       * 5 giảng viên.
       * 5 môn (có số tiết tương ứng là {2,3,4,5,6}.
       * Nhóm lớp x phòng học x môn = mảng 3 chiều [giangvien, nhomlop, mon]. Với giangvien: dòng; nhomlop: cột; mon: chiều sâu.
       * Tính mon = index / (5 x 10); giangvien= index / 10; nhomlop = index % 10. index bắt đầu từ 0.
       * Tính giá trị sự kiện = (mon x (5x10) + giangviên x 10 + nhomlop) - 1. index bắt đầu từ 0.
       * 5 phòng học. Trong đó, 4 phòng có sức chứa là 50 và 1 phòng có sức chứa là 100.
       * 5 ngày trong tuần (từ thứ 2 đến thứ 6).
       * 10 buổi vì mỗi ngày 2 buổi.
       * 250 tiết = 50 tiết x 5 phòng. Vì mỗi buổi 5 tiết.
       * 10 cá thể trong quần thể.
       * 50 sự kiện [nhomlop, phonghoc, mon] x 4 (số tiết trung bình của mỗi môn) ~ 200 tiết cần phải được sắp xếp.
        */

        // Số Giảng viên.
        public int NumTeacher = 3;
        // Số lớp.
        public int NumClass = 3;
        // Số môn.
        public int NumSubject = 2;
        // Số phòng.
        public int NumRoom = 2;
        // Số ngày trong tuần.
        public int NumDay = 3;
        // Số buổi trong ngày.
        public int NumSession = 2;
        // Số tiết trong buổi.
        public int NumPeriod = 5;
        // Số tiết trong tuần = số ngày x số buổi x số tiết.
        public int NumWeekPeriod { get { return NumDay * NumSession * NumPeriod; } }
        // Tổng số tiết trong tuần = số tiết trong tuần x số phòng.
        public int NumWeekPeriodTotal { get { return NumDay * NumSession * NumPeriod * NumRoom; } }
        // Số lượng thời khóa biểu duy trì.
        public int NumElements = 500;
        // Số lần lặp để tìm kết quả.
        public int NumLoopMax = 50000;
        // Số thời khóa biểu có độ thích nghi tốt cần giữ lại sau mỗi lần lặp.
        public int NumElementsKeep = 2;
        // Số sự kiện < NumTeacher x NumClass x (tổng số tiết của các môn (1 + 2 + 3 + 4))
        // = 2 x 3 x (2 + 2 + 2 + 2) = 48.
        // Tỉ lệ lai ghép trên phân nửa quần thể.
        public int iLaiGhepRate = 50;
        // Tỉ lệ đột biến trên phân nửa quần thể.
        public int iDotBienRate = 50;


        public List<int> LstUnitClass = new List<int>(); // Sỉ số của nhóm lớp.
        public List<int> LstTeacher = new List<int>(); // Giảng viên.
        public List<int> LstSubject = new List<int>(); // Môn học.
        public List<int> LstSubjectUnit = new List<int>(); // Số tiết ứng với mỗi môn học (LstSubject).
        public List<int> LstUnitRoom = new List<int>(); // Sức chứa của phòng học.
        public List<int> LstRoom = new List<int>(); // Phòng học.
        public List<int> LstUnit = new List<int>(); // Tiết học.
        public List<int> LstEvent = new List<int>(); // Tập sự kiện.

        private static InputData _Data;
        public static InputData Instance()
        {
            if(_Data == null)
                _Data = new InputData();
            return _Data;;
        }

        private InputData()
        {
            InitData();
            InitEvents();
        }// end InputData

        private void InitData()
        {
            // Khởi tạo giảng viên.
            for (int i = 0; i < NumTeacher; i++)
            {
                LstTeacher.Add(i);
            }
            // Khởi tạo nhóm lớp.
            LstUnitClass.Add(100);// Nhóm sv đầu tiên có sỉ số là 100.
            for (int i = 1; i < NumClass; i++)
            {
                LstUnitClass.Add(50);// Mỗi lớp có sỉ số là 50.
            }
            // Khởi tạo môn học.
            for (int i = 0; i < NumSubject; i++)
            {
                LstSubject.Add(i);
                if (i % 3 == 0)
                    LstSubjectUnit.Add(2);// Số tiết của môn học tương ứng (2,2,2,2,2).
                else
                    LstSubjectUnit.Add(3);// Số tiết của môn học tương ứng (2,2,2,2,2).
            }
            // Khởi tạo phòng học.
            LstUnitRoom.Add(100);// Phòng đầu tiên có sức chứa là 100.
            for (int i = 1; i < NumRoom; i++)
            {
                LstUnitRoom.Add(50);
                LstRoom.Add(i);
            }
        }

        public void InitEvents()
        {
            LstEvent.Clear();
            // Khởi tạo tập sự kiện với giá trị ngẫu nhiên.
            Random radom = new Random();
            for (int i = 0; i < NumTeacher; i++)
            {
                for (int j = 0; j < NumClass; j++)
                {
                    for (int k = 0; k < NumSubject; k++)
                    {
                        if (radom.Next(0, 5) > 0)
                        {
                            int iEventValue = eventValue(i, j, k);
                            // Lặp lại số tiết cho từng môn.
                            int iNumUnit = LstSubjectUnit[k];
                            for (int n = 0; n < iNumUnit; n++)
                            {
                                LstEvent.Add(iEventValue);
                            }// end for
                        }// end if
                    }// end for k
                }// end for j
            }// end for i
        }


        #region Quy đổi từ mảng 2 chiều (tiết học).
        // Đổi từ vị trí tiết ra tiết chưa có phòng.
        public int indexUnitValue(int pUnitValueTotal)
        {
            return pUnitValueTotal % (NumDay * NumSession * NumPeriod);
        }
        // Đổi từ vị trí tiết ra phòng.
        public int indexRoom(int pUnitValueTotal)
        {
            return pUnitValueTotal / (NumDay * NumSession * NumPeriod);
        }
        // Đổi từ tiết chưa có phòng và phòng ra tiết có phòng.
        public int unitValueTotal(int pIndexUnit, int pIndexRoom)
        {
            return (pIndexRoom * NumDay * NumSession * NumPeriod) + pIndexUnit;
        }
        #endregion Quy đổi từ mảng 2 chiều (tiết học).

        #region Quy đổi giá trị từ mảng 3 chiều.
        // Lấy giá trị của sự kiện từ chỉ số của mảng 3 chiều.
        public int eventValue(int pTeacher, int pClass, int pSubject)
        {
            return (pSubject * (NumTeacher * NumClass) + pTeacher * (NumClass) + pClass);
        }
        // Lấy index (tính từ 0) của giangvien từ mảng 3 chiều.
        public int indexTeacher(int pValue)
        {
            return (pValue % (NumTeacher * NumClass)) / NumClass;
        }
        // Lấy index (tính từ 0) của nhomlop từ mảng 3 chiều.
        public int indexClass(int pValue)
        {
            return (pValue % (NumTeacher * NumClass)) % NumClass;
        }
        // Lấy index (tính từ 0) của mon từ mảng 3 chiều.
        public int indexSubject(int pValue)
        {
            return pValue / (NumTeacher * NumClass);
        }
        #endregion Quy đổi giá trị từ mảng 3 chiều.

    }// end Class
}// end namespace
